<!--文件上传组件-->
<template>
  <a-upload
    v-bind="props"
    :file-list="fileList"
    :before-upload="beforeUpload"
    @change="handleChange"
    @preview="previewFile"
    @remove="handleRemove"
  >
    <!--文本上传-->
    <template v-if="listType === 'text'">
      <a-button>
        <loading-outlined v-if="loading"></loading-outlined>
        <upload-outlined v-else></upload-outlined>
        点击上传
      </a-button>
    </template>
    <!--图片上传-->
    <template v-if="listType !== 'text'">
      <loading-outlined v-if="loading"></loading-outlined>
      <upload-outlined v-else></upload-outlined>
      点击上传
    </template>
  </a-upload>
</template>

<script lang="ts" setup>
import { UploadOutlined, LoadingOutlined } from '@ant-design/icons-vue'
import { ref, defineEmits, withDefaults, defineProps, watch, nextTick } from 'vue'
import { UploadProps, message, UploadChangeParam, UploadFile, ProgressProps } from 'ant-design-vue'
import { deleteFile, deleteFileByUrl, fileUpload } from '../../../api/system/file'
import { getFilePath } from '../../../utils/file-util'
import { FileVo } from '@/entity/system/file'
export interface FileProps {
  // 初始文件
  value?: string
  // 文件是否可删除
  deleteAble?: boolean
  /**
   *  接受上传的文件类型
   *  .pdf,.doc,.docx,
   *  image/*
   *  audio/* video/*
   * "image/apng",
   * "image/bmp",
   * "image/gif",
   * "image/jpeg",
   * "image/pjpeg",
   * "image/png",
   * "image/svg+xml",
   * "image/tiff",
   * "image/webp",
   * "image/x-icon"
   */
  accept?: string
  // 上传的地址
  action?: string | Function
  // 文件名称
  name?: string
  // 请求头
  headers?: object
  // 支持上传文件大小
  size?: number
  // 上传列表的内建样式，支持三种基本样式 text, picture 和 picture-card
  listType?: 'text' | 'picture' | 'picture-card'
  // 支持上传文件夹
  directory?: boolean
  //  限制上传数量。当为 1 时，始终用最新上传的文件代替当前文件
  maxCount?: number
  // 上传请求的 http method
  method?: string
  // 是否支持多选文件，ie10+ 支持。开启后按住 ctrl 可选择多个文件。
  multiple?: boolean
  // 点击打开文件对话框
  openFileDialogOnClick?: boolean
  // 是否禁用
  disabled?: boolean
  // 上传所需参数或返回上传参数的方法
  data?: (file: UploadFile) => object | object
  // 通过覆盖默认的上传行为，可以自定义自己的上传实现
  customRequest?: Function
  // 自定义进度条样式
  progress?: any
  // 自定义下载 icon
  downloadIcon?: string
  // 自定义显示 icon
  iconRender?: string
  // 自定义缩略图是否使用 <img /> 标签进行显示
  isImageUrl?: (file: UploadFile) => boolean
  // 自定义上传列表项
  itemRender?: {
    originNode: object
    file: UploadFile
    fileList: object[]
    actions: { download: Function; preview: Function; remove: Function }
  }
  // 自定义文件预览逻辑
  previewFile?: (file: UploadFile) => Promise<string>
  // 自定义预览 icon
  previewIcon?: string
  // 自定义删除 icon
  removeIcon?: string
  // 是否展示 uploadList, 可设为一个对象，用于单独设定 showPreviewIcon, showRemoveIcon 和 showDownloadIcon
  showUploadList?:
    | boolean
    | {
        showPreviewIcon?: boolean
        showRemoveIcon?: boolean
        showDownloadIcon?: boolean
      }
  // 服务端渲染时需要打开这个
  supportServerRender?: boolean
  // 上传请求时是否携带 cookie
  withCredentials?: boolean
}
const props = withDefaults(defineProps<FileProps>(), {
  name: '',
  action: '',
  headers: () => Object,
  showUploadList: true,
  directory: false,
  accept: 'image/*',
  size: 3,
  listType: 'picture-card',
  deleteAble: false,
  openFileDialogOnClick: true,
  progress: {
    strokeColor: {
      '0%': '#108ee9',
      '100%': '#87d068',
    },
    strokeWidth: 3,
    format: (percent: number) => `${parseFloat(percent.toFixed(2))}%`,
  },
})
// 文件列表改变时
const emit = defineEmits<{ (e: 'change', value: string): void }>()
watch(props, () => {
  nextTick(() => {
    // 初始化文件列表

    fileList.value = props.value ? props.value.split(',').map((val) => ({ url: getFilePath(val), status: 'done' })) : []
  })
})
// 文件上传列表
// @ts-ignore
const fileList = ref<UploadProps['fileList'][number]>([])
// 上传状态
const loading = ref<boolean>(false)
// 文件上传之前的验证
// @ts-ignore
// 是否失败
const isError = ref<boolean>(false)
const beforeUpload = (file: UploadProps['fileList'][number]) => {
  // 文件大小验证
  const isLtLength = file.size / 1024 / 1024 < props.size
  if (!isLtLength) {
    message.error('文件必须小于' + props.size + 'MB!')
    isError.value = true
  }
  // 如果用户传递了action，这边走用户的上传
  return props.action ? true : false
}

/**
 * 更新当前已完成的文件值
 * @param state 状态值（默认只更新完成的值）
 */
function updateValue(state: string = 'done') {
  // 已字符串返回图片路径
  const value = fileList.value.filter((val: any) => val.status === state).map((val: any) => val.url)
  emit('change', value.join(','))
}

// 文件上传
const handleChange = async (info: UploadChangeParam) => {
  const { file } = info
  // 默认处理
  if (file.status === 'uploading') {
    loading.value = true
  }
  if (file.status === 'done') {
    message.success('文件上传成功!')
    loading.value = false
  }
  if (file.status === 'error') {
    message.error('文件上传失败!')
    loading.value = false
  }
  if (file.status === 'removed') {
    await handleRemove(file as UploadFile & { id: string })
    loading.value = false
  }
  // 如果前置验证不通过，则停止上传
  if (isError.value === true) {
    isError.value = false
    loading.value = false
    return
  }
  // 如果用户不是走的action上传并且也没有自定义上传请求。则默认走本地上传
  if (!file.status && !props.customRequest) {
    file.status = 'uploading'
    loading.value = true
    const formData = new FormData()
    formData.append('file', file as unknown as string)
    await fileUpload(formData).then((res) => {
      if (res.code == 200) {
        message.success('文件上传成功!')
        let file = res.data as FileVo
        // 图片路径回传
        file.url = file.fileUrl
        file.id = file.id
        file.status = 'done'
        // 如果文件已超过最大限制，则始终替换最后的那个文件
        props.maxCount == fileList.value.length
          ? (fileList.value[fileList.value.length - 1] = file)
          : fileList.value.push(file)
        updateValue()
        loading.value = false
      }
    })
  }
}
/**
 * 文件移除
 * @param file 文件
 */
const handleRemove = async (file: UploadFile & { id: string }) => {
  // 过滤掉移除的值
  fileList.value = fileList.value.filter((val: UploadFile) => val.status !== 'removed')
  // 真正将文件删除
  if (props.deleteAble) {
    if (file.id) {
      await deleteFile(file.id as unknown as string)
    } else if (file.url) {
      await deleteFileByUrl(file.url as unknown as string)
    }
  }
  updateValue()
}
</script>

<style scoped lang="scss">
// 上传样式
.avatar-uploader > .ant-upload {
  width: 128px;
  height: 128px;
}
.ant-upload-select-picture-card i {
  font-size: 32px;
  color: #999;
}

.ant-upload-select-picture-card .ant-upload-text {
  margin-top: 8px;
  color: #666;
}
</style>
